﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.Xsl;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;

namespace SharePointPowerToys
{
    /// <summary>
    /// PowerQueryWebPart
    /// </summary>
    public class PowerQueryWebPart : Microsoft.SharePoint.WebPartPages.WebPart, IWebEditable
    {
        #region properties

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("A class that must Implement the IPowerQueryDataProviderInterface (case sensitive)"),
            WebDisplayName("Data Provider Assembly and Class"),
            Category("Data Provider")]
        public string DataProviderAssemblyClass
        {
            get;
            set;
        }

        private string _dataProviderParameters = string.Empty;
        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("Use this to send in NameValue pairs specific to your webpart. Send in the pairs in a string via the following format: 'Tag=Value|Tag=Value|Tag=Value'"),
            WebDisplayName("Data Provider Parameters"),
            Category("Data Provider")]
        public string DataProviderParameters
        {
            get
            {
                return _dataProviderParameters;
            }
            set
            {
                _dataProviderParameters = value;
                DataProviderParametersCollection = Helpers.BuildNameValueCollection(_dataProviderParameters, '|');
            }
        }

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("The URL to the Site to retrieve data from. (Default: Current Site)"),
            WebDisplayName("Site URL"),
            Category("Data Provider")]
        public string SiteUrl
        {
            get;
            set;
        }

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("The Name of the List to retrieve data from. (Default: Pages)"),
            WebDisplayName("List Name"),
            DefaultValue("Pages"),
            Category("Data Provider")]
        public string ListName
        {
            get;
            set;
        }

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("Maximum number of items to return, 0 equals All."),
            WebDisplayName("Item Limit"),
            Category("Data Provider")]
        public long ItemLimit
        {
            get;
            set;
        }

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("Number of seconds to cache data source."),
            WebDisplayName("Seconds to Cache Data Source"),
            DefaultValue(0),
            Category("Data Provider")]
        public long CacheSourceSeconds
        {
            get;
            set;
        }

        string _xslTransformFile = string.Empty;
        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("A URL to an XSL stylesheet that will transform the XML or just FileName to use local site /Style Library/XSL Style Sheets"),
            WebDisplayName("XSL Transform File URL"),
            Category("Data Transform")]
        public string XslTransformFile
        {
            get
            {
                return _xslTransformFile;
            }
            set
            {
                _xslTransformFile = value;
                PartCacheWrite(Storage.Shared, "Results_" + ID, null, TimeSpan.FromSeconds(0));
            }
        }

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("Number of seconds to cache results."),
            WebDisplayName("Seconds to Cache Results"),
            DefaultValue(0),
            Category("Configuration")]
        public long CacheResultsSeconds
        {
            get;
            set;
        }

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("Show refresh Time."),
            WebDisplayName("Show Refresh Time"),
            DefaultValue(false),
            Category("Configuration")]
        public bool ShowRefreshTime
        {
            get;
            set;
        }

        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(true),
            WebDescription("When in Debug Mode all XML is output as plain text."),
            WebDisplayName("Debug Mode"),
            Category("Configuration")]
        public bool DebugMode
        {
            get;
            set;
        }

        string _configUrls = string.Empty;
        [Personalizable(PersonalizationScope.Shared)]
        public string ConfigUrls
        {
            get
            {
                return _configUrls;
            }
            set
            {
                _configUrls = value;
                PartCacheWrite(Storage.Shared, "Results_" + ID, null, TimeSpan.FromSeconds(0));
            }
        }

        String selectedSettingKey = string.Empty;
        [Personalizable(PersonalizationScope.Shared),
            WebBrowsable(false)]
        public String SelectedSettingKey
        {
            get
            {
                return selectedSettingKey;
            }
            set
            {
                selectedSettingKey = value;
            }
        }
        #endregion

        private NameValueCollection DataProviderParametersCollection = new NameValueCollection();

        EditorPartCollection IWebEditable.CreateEditorParts()
        {
            List<EditorPart> editors = new List<EditorPart>();
            editors.Add(new DefaultOptions(ID));
            return new EditorPartCollection(editors);
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            string results = string.Empty;
            XslCompiledTransform xslTransform = null;
            XmlReader xmlFromQuery = null;

            // Results Cached?
            if (CacheResultsSeconds > 0 && PartCacheRead(Storage.Shared, "Results_" + ID) != null)
            {
                try
                {
                    //writer = (HtmlTextWriter)HttpContext.Current.Cache["Results_" + this.ID];
                    //writer.Write((HtmlTextWriter)PartCacheRead(Storage.Shared, "Results_" + this.ID));
                    results = PartCacheRead(Storage.Shared, "Results_" + ID).ToString();
                }
                catch (Exception ex)
                {
                    Controls.Add(new LiteralControl("Error retrieving Cached Results (Results_" + ID + "): " + ex.Message));
                    return;
                }
            }
            else
            {
                // Data Source Cached?
                if (CacheSourceSeconds > 0 && PartCacheRead(Storage.Shared, "Source_" + ID) != null && PartCacheRead(Storage.Shared, "Source_" + ID).ToString() != string.Empty)
                {
                    //string xml = this.PartCacheRead(Storage.Shared, "Source_" + this.ID).ToString();
                    //StringReader sr = new StringReader(xml);
                    //System.Xml.XmlReader xr = new System.Xml.XmlTextReader(sr);
                    //xmlFromQuery = new System.Xml.XmlTextReader(sr);
                    xmlFromQuery = XmlReader.Create(new System.IO.StringReader(PartCacheRead(Storage.Shared, "Source_" + ID).ToString()));
                }
                else
                {
                    if (!String.IsNullOrEmpty(DataProviderAssemblyClass))
                    {
                        Assembly assem = Assembly.LoadWithPartialName(DataProviderAssemblyClass);
                        //Assembly assem = Assembly.GetExecutingAssembly();
                        if (assem != null)
                        {
                            Type xmlClass = assem.GetType(DataProviderAssemblyClass);
                            if (xmlClass != null)
                            {
                                IPowerQueryDataProviderInterface customQueryClass;
                                try
                                {
                                    if (HttpContext.Current.Cache[DataProviderAssemblyClass] != null)
                                    {
                                        customQueryClass = (IPowerQueryDataProviderInterface)HttpContext.Current.Cache[DataProviderAssemblyClass];
                                    }
                                    else
                                    {
                                        customQueryClass = (IPowerQueryDataProviderInterface)Activator.CreateInstance(xmlClass);
                                        HttpContext.Current.Cache.Add(DataProviderAssemblyClass, customQueryClass, null, DateTime.Now.AddMonths(1), TimeSpan.Zero, CacheItemPriority.Normal, null);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Controls.Add(new LiteralControl("Error loading DataProvider: " + DataProviderAssemblyClass + " - " + ex.Message));
                                    return;
                                }

                                if (SiteUrl != null && SiteUrl.StartsWith("/"))
                                {
                                    SiteUrl = SPContext.Current.Web.Url + SiteUrl;
                                }

                                customQueryClass.SiteUrl = SiteUrl;
                                customQueryClass.ListName = ListName;
                                customQueryClass.DebugMode = DebugMode;
                                customQueryClass.ItemLimit = ItemLimit;
                                customQueryClass.Parameters = DataProviderParametersCollection;
                                customQueryClass.WebParameters = HttpContext.Current.Request.QueryString;
                                xmlFromQuery = customQueryClass.GetXmlReader();
                            }
                            else
                            {
                                Controls.Add(new LiteralControl("XmlQueryClass Type was not found: " + DataProviderAssemblyClass));
                                return;
                            }
                        }
                        else
                        {
                            Controls.Add(new LiteralControl("DataProvider Assembly was not found: " + DataProviderAssemblyClass));
                            return;
                        }
                    }
                    else
                    {
                        if (DataProviderParametersCollection != null && (DataProviderParametersCollection["XmlFileName"] != null || DataProviderParametersCollection["sourceUrl"] != null))
                        {
                            try
                            {
                                XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
                                xmlReaderSettings.ProhibitDtd = false;
                                if (DataProviderParametersCollection["XmlFileName"] != null)
                                {
                                    xmlFromQuery = XmlReader.Create(new System.IO.StringReader(Helpers.GetSpFile(DataProviderParametersCollection["XmlFileName"])), xmlReaderSettings);
                                }
                                else if (DataProviderParametersCollection["sourceUrl"] != null)
                                {
                                    xmlFromQuery = XmlReader.Create(DataProviderParametersCollection["sourceUrl"], xmlReaderSettings);
                                }
                            }
                            catch (Exception ex)
                            {
                                Controls.Add(new LiteralControl("Could not load XmlFileName: " + DataProviderParametersCollection["XmlFileName"] + " - Error: " + ex.Message));
                                return;
                            }
                        }
                    }

                    // Cache XmlReader if necessary
                    if (CacheSourceSeconds > 0 && xmlFromQuery != null)
                    {
                        try
                        {
                            xmlFromQuery.MoveToContent();
                            PartCacheWrite(Storage.Shared, "Source_" + ID, xmlFromQuery.ReadOuterXml(), TimeSpan.FromSeconds(CacheSourceSeconds));
                            xmlFromQuery = XmlReader.Create(new System.IO.StringReader(PartCacheRead(Storage.Shared, "Source_" + ID).ToString()));
                        }
                        catch (Exception ex)
                        {
                            Controls.Add(new LiteralControl("Could not cache DataSource - Error: " + ex.Message));
                            return;
                        }

                    }
                }

                // Load the XSLT
                if (!String.IsNullOrEmpty(XslTransformFile))
                {
                    try
                    {
                        xslTransform = Helpers.TransformLoad(XslTransformFile);
                    }
                    catch (Exception ex)
                    {
                        Controls.Add(new LiteralControl("Could not load XSL style sheet (Did you publish Major Version?): " + XslTransformFile + " - Error: " + ex.Message));
                        return;
                    }
                }

                // Run the XSLT
                if (xslTransform != null)
                {
                    try
                    {
                        results = Helpers.TransformGetString(xslTransform, xmlFromQuery);
                    }
                    catch (Exception ex)
                    {
                        Controls.Add(new LiteralControl("Could not load XML from Transformation using XSL Style Sheet: " + XslTransformFile + " - Error: " + ex.Message));
                        return;
                    }
                }

                // CacheResults
                if (CacheResultsSeconds > 0)
                {
                    results = string.Concat(results, "<br/>Last Updated:" + DateTime.Now.ToShortTimeString());
                    //HttpContext.Current.Cache.Add("Results_" + this.ID, writer, null, DateTime.Now.AddSeconds(CacheResultsSeconds), TimeSpan.Zero, CacheItemPriority.Normal, null);
                    PartCacheWrite(Storage.Shared, "Results_" + ID, results, TimeSpan.FromSeconds(CacheResultsSeconds));
                }
            }
            writer.Write(results);
            base.RenderContents(writer);
        }
    }
}
